package com.scilvet.reportcomposer.account;

import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;


public class MapAccountStore implements AccountStore {
	
	Logger logger = Logger.getLogger(MapAccountStore.class);
	
	private AccountLayer root;
	
	public MapAccountStore() {
		root = new AccountLayer(AccountID.ALL_ACCOUNTS);
	}
	
	@Override
	public void addToAccount(AccountID accountID, Amount credit, Amount debit, Amount balance) {
		
		if (accountID == null || credit == null || debit == null || balance == null) {
			logger.warn("addAccount method called with null parameters");
			return;
		}
		 
		root.getAccount().addAmount(credit, debit, balance);
		
		AccountLayer currentLayer = root;
		int level = 1;
		for (String idComponent : accountID) {
			
			if (AccountID.isWildcard(idComponent)) {
				return;
			}
			
			AccountLayer nextLayer = currentLayer.getNextLayer(idComponent);
			if (nextLayer == null) {
				nextLayer = currentLayer.addAccountLayer(accountID.getWildcard(level), idComponent);
			}
			
			nextLayer.getAccount().addAmount(credit, debit, balance);
			currentLayer = nextLayer;
			level++;
		}
		
	}

	@Override
	public void addToAccount(AccountID accountID, AmountType direction, Amount amount) {
		
		if (accountID == null || direction == null || amount == null) {
			logger.warn("addAccount method called with null parameters");
			return;
		}
		 
		root.getAccount().addAmount(amount, direction);
		
		AccountLayer currentLayer = root;
		int level = 1;
		for (String idComponent : accountID) {
			
			if (AccountID.isWildcard(idComponent)) {
				return;
			}
			
			AccountLayer nextLayer = currentLayer.getNextLayer(idComponent);
			if (nextLayer == null) {
				nextLayer = currentLayer.addAccountLayer(accountID.getWildcard(level++), idComponent);
			}
			nextLayer.getAccount().addAmount(amount, direction);
			currentLayer = nextLayer;
		}
		
	}
	
	@Override
	public Account getAccount(AccountID accountID) {
		
		if (accountID == null) {
			logger.warn("getAccount method called with null parameters");
			return null;
		}
		
		AccountLayer currentLayer = root;
		
		for (String idComponent : accountID) {
			
			AccountLayer nextLayer = currentLayer.getNextLayer(idComponent);
			if (nextLayer == null) {
				return null;
			}
			
			currentLayer = nextLayer;
		}
		
		return currentLayer.getAccount();
	}

	@Override
	public Amount getAccountValue(AccountID accountID, AmountType direction) {
		
		if (accountID == null || direction == null) {
			logger.warn("accountValue method called with null parameters");
			throw new NullPointerException();
		}
		
		AccountLayer currentLayer = root;
		for (String idComponent : accountID) {
			if (AccountID.isWildcard(idComponent)) {
				break;
			}
			
			currentLayer = currentLayer.getNextLayer(idComponent);
			if (currentLayer == null) {
				return Amount.ZERO;
			}
		}
		
		return currentLayer.getAccount().getAmount(direction);
	}

	
	private class AccountLayer {
		
		private Account account;
		private Map<String, AccountLayer> accountMap;
		
		private AccountLayer(AccountID accountID) {
			this.account = new Account(accountID);
			this.accountMap = new HashMap<String, MapAccountStore.AccountLayer>();
		}
		
		private AccountLayer addAccountLayer(AccountID accountID, String layerID) {
			AccountLayer newAccountLayer = new AccountLayer(accountID);
			accountMap.put(layerID, newAccountLayer);
			
			return newAccountLayer;
		}
		
		private Account getAccount() {
			return account;
		}
		
		private AccountLayer getNextLayer(String layerID) {
			return accountMap.get(layerID);
		}
	}
	
}